package com.xwj.seckill.aspect;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;

/**
 * 将加锁操作放到切面中，确保事务提交后释放锁
 * 无论是直接使用zk加锁还是将zk锁+aop的方式都不会发生超卖
 * @author xwj
 * @date 2020/5/7
 */
@Component
@Aspect
@Order(1)
public class ZookeeperServiceLockAspect {
    @Autowired
    private CuratorFramework client;

    @Pointcut("@annotation(com.xwj.seckill.annotation.ZookeeperServiceLock)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        Long userId = null;
        String bookNo = null;
        Object[] args = joinPoint.getArgs();
        try {
            for (Object o : args) {
                Field field = o.getClass().getDeclaredField("userId");
                if (field != null) {
                    field.setAccessible(true);
                    Object object = field.get(o);
                    if(object != null) {
                        userId = (Long) object;
                    }
                }
                field = o.getClass().getDeclaredField("bookNo");
                if (field != null) {
                    field.setAccessible(true);
                    Object object = field.get(o);
                    if(object != null) {
                        bookNo = (String) object;
                    }
                }
            }
        } catch (Exception e) {}
        final String path = "/ZookeeperService/lock";
        InterProcessMutex mutex = new InterProcessMutex(client, path);
        try {
            if (mutex.acquire(10L, TimeUnit.SECONDS)) {
                obj = joinPoint.proceed();
            }
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException();
        } finally {
            try {
                mutex.release();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return obj;
    }
}
